﻿using System;
using System.Collections.Generic;
using System.IO;
using DirectShowLib;
using DirectShowLib.DES;
using System.Runtime.InteropServices;
using MediaInfoNet;

namespace MediaPlayer
{
    public class AudioStreamInfo
    {
        public Guid SubType { get; internal set; }
        public int Size { get; internal set; }
        public int AverageBytesPerSec { get; internal set; }
        public int BlockAlign { get; internal set; }
        public int Channels { get; internal set; }
        public int SamplesPerSecond { get; internal set; }
        public int BitsPerSample { get; internal set; }
        public int FormatTag { get; internal set; }
        public int Khz { get; internal set; }
        public int Kbps { get; internal set; }
        public TimeSpan Duration { get; internal set; }
    }

    public class VideoStreamInfo
    {
        public Guid SubType { get; internal set; }
        public int Width { get; internal set; }
        public int Height { get; internal set; }
        public double FrameRate { get; internal set; }
        public int Frames { get; internal set; }
        public TimeSpan Duration { get; internal set; }
    }

    public class MediaFile
    {
        public string FileName { get; set; }
        public string FilePath { get; set; }
        public string Folder { get; set; }

        public List<AudioStreamInfo> Audio = new List<AudioStreamInfo>();
        public List<VideoStreamInfo> Video = new List<VideoStreamInfo>();
        public Dictionary<string, string> Metadata = new Dictionary<string, string>();
        public TimeSpan Duration = TimeSpan.FromSeconds(0);
        public System.Drawing.Image Art = null;

        public MediaFile(string file)
        {
            if (!System.IO.File.Exists(file)) throw new ArgumentException(file);

            var mediaFile = new MediaInfoNet.MediaFile(file);

            foreach (var property in mediaFile.General.Properties)
            {
                this.Metadata.Add(property.Key, property.Value);
            }

            foreach (var audio in mediaFile.Audio)
            {
                foreach (var property in audio.Properties)
                {
                    if (this.Metadata.ContainsKey(property.Key))
                    {
                        if (this.Metadata[property.Key] != property.Value)
                        {
                            this.Metadata[property.Key] = property.Value;
                        }
                    }
                    else
                    {
                        this.Metadata.Add(property.Key, property.Value);
                    }
                }
            }

            if (mediaFile.CoverArt != null)
            {
                this.Art = mediaFile.CoverArt;
            }


            this.FilePath = Path.GetFullPath(file);
            this.FileName = Path.GetFileName(this.FilePath);
            this.Folder = Path.GetDirectoryName(this.FilePath);

            IMediaDet mediaDet = (IMediaDet)new MediaDet();

            try
            {
                DsError.ThrowExceptionForHR(mediaDet.put_Filename(file));
            }
            catch
            {
                return;
            }

            int i = 0;
            Guid streamGuid = Guid.Empty;
            int numStreams;

            mediaDet.get_OutputStreams(out numStreams);

            while (i < numStreams)
            {
                mediaDet.put_CurrentStream(i++);
                mediaDet.get_StreamType(out streamGuid);

                if (streamGuid == DirectShowLib.MediaType.Video)
                {
                    AMMediaType mediaType = new AMMediaType();
                    mediaDet.get_StreamMediaType(mediaType);
                    if (mediaType.formatType != FormatType.VideoInfo) throw new Exception("Unknown video format");

                    VideoInfoHeader videoInfo = (VideoInfoHeader)Marshal.PtrToStructure(mediaType.formatPtr, typeof(VideoInfoHeader));

                    VideoStreamInfo video = new VideoStreamInfo();
                    video.Width = videoInfo.BmiHeader.Width;
                    video.Height = videoInfo.BmiHeader.Height;

                    double frameRate;
                    mediaDet.get_FrameRate(out frameRate);
                    video.FrameRate = frameRate;

                    double length;
                    mediaDet.get_StreamLength(out length);
                    video.Duration = TimeSpan.FromSeconds(length);

                    if (video.Duration > this.Duration)
                    {
                        this.Duration = video.Duration;
                    }

                    video.Frames = (int)(frameRate * length);

                    video.SubType = mediaType.subType;
                    this.Video.Add(video);

                    DsUtils.FreeAMMediaType(mediaType);
                    mediaType = null;
                }
                else if (streamGuid == DirectShowLib.MediaType.Audio)
                {
                    AMMediaType mediaType = new AMMediaType();
                    mediaDet.get_StreamMediaType(mediaType);
                    if (mediaType.formatType != FormatType.WaveEx) throw new Exception("Unknown audio format");

                    var audioInfo = Marshal.PtrToStructure(mediaType.formatPtr, typeof(WaveFormatEx)) as WaveFormatEx;

                    AudioStreamInfo audio = new AudioStreamInfo();
                    audio.Size = audioInfo.cbSize;
                    audio.AverageBytesPerSec = audioInfo.nAvgBytesPerSec;
                    audio.BlockAlign = audioInfo.nBlockAlign;
                    audio.Channels = audioInfo.nChannels;
                    audio.SamplesPerSecond = audioInfo.nSamplesPerSec;
                    audio.BitsPerSample = audioInfo.wBitsPerSample;
                    audio.FormatTag = audioInfo.wFormatTag;
                    audio.Khz = audio.SamplesPerSecond / 1000;
                    audio.Kbps = (audio.AverageBytesPerSec * 8) / 1000;

                    double length;
                    mediaDet.get_StreamLength(out length);
                    audio.Duration = TimeSpan.FromSeconds(length);

                    if (audio.Duration > this.Duration)
                    {
                        this.Duration = audio.Duration;
                    }

                    audio.SubType = mediaType.subType;
                    this.Audio.Add(audio);

                    DsUtils.FreeAMMediaType(mediaType);
                    mediaType = null;
                }
                else
                {
                    throw new Exception("Unknown Media Type: " + streamGuid);
                }
            }

            if (this.Duration == TimeSpan.FromSeconds(0))
            {
                // Get more details
                FileInfo a = new System.IO.FileInfo(file);
                //Dictionary<string, string> b = a.GetDetails();
                KeyValuePair<string, string> c = a.GetDetail(FileDetails.Length);

                if (c.Value != null && c.Value != string.Empty)
                {
                    this.Duration = TimeSpan.Parse(c.Value);
                }
            }
        }
    }
}
